/**
 * \file task.c
 * \author Davide Gessa
 * \date 10-12-09
 */
#include <task/task.h>
#include <video.h>
#include <debug.h>
#include <task/term.h>

/**
 * Metodo round robin
 */
void task_switch_round_robin()
{
	if(task_current != NULL)
	{			
		//video_puts("round: ");
		//video_putint(task_get_pid());
		
		// Salviamo i registri del task corrente

	   
	   task_current->state = TASK_STATE_RUNNABLE;
			
		// Cambiamo task
		do
		{
			task_current = task_current->next;
			
			if(task_current == NULL) 
				task_current = task_list;
		} while(task_current->state != TASK_STATE_RUNNABLE);
		
		task_current->state = TASK_STATE_RUNNING;
		
		// Ripristiniamo i registri del task corrente

			
	}
}


/**
 * Metodo priorita'
 */
void task_switch_priority()
{
}


/**
 * Switcha tra i processi in esecuzione
 * 
 * \param regs registri durante l'interrupt del pit
 */
void task_switch(regs_t *regs)
{	
	// Chiama la funzione selezionata come metodo di switching
	if(task_switch_f != NULL)
	{
		void (*switching)();
		switching = task_switch_f;
		switching();	
	}
}


/**
 * Inizializza il gestore dei task
 * 
 * \param st metodo di switching
 */
void task_init(uint8_t st)
{
	DEBUGP("task_init()\n");
		
	switch(st)
	{
		//case TASK_SWITCH_PRIORITY:
		//	task_switch_f = &task_switch_priority;
		//	break;
		case TASK_SWITCH_ROUND_ROBIN:
			task_switch_f = &task_switch_round_robin;
			break;
			
		default:
			video_puts("Panic!\n");
			while(1);
	}
	
	task_list = NULL;
	task_current = NULL;
	task_cur_pid = 0;
}


/**
 * Restituisce il pid del processo corrente
 * 
 * \return pid del processo corrente
 */
uint32_t task_get_pid(){ return task_current->pid; }



/**
 * Restituisce dato il pid, il puntatore al task
 * 
 * \param pid processo
 * \return struttura del processo
 */
task_t *task_get_from_pid(pid_t pid)
{
	task_t *t = task_list;
	
	while(t != NULL)
	{
		if(t->pid == pid) return t;
		t = t->next;
	}
	return (task_t *) NULL;
}


/**
 * Restituisce il nome del task
 * 
 * \param pid processo
 * \return nome del processo
 */
char *task_get_name(pid_t pid)
{
	task_t *t = task_get_from_pid(pid);
	
	if(t == NULL) return (char *) NULL;
	return t->name;
}


/**
 * Restituisce l'user id del processo
 * 
 * \param pid processo
 * \return uid del processo
 */
uid_t task_get_uid(pid_t pid)
{
	task_t *t = task_get_from_pid(pid);
	
	if(t == NULL) return -1;
	return t->pid;
}


/**
 * Restituisce lo stato del processo
 * 
 * \param pid processo
 * \return stato del processo
 */
uint8_t task_get_state(pid_t pid)
{
	task_t *t = task_get_from_pid(pid);
	
	if(t == NULL) return -1;
	return t->state;
}


/**
 * Mette in pausa il processo
 * 
 * \param pid processo
 */
uint32_t task_pause(pid_t pid)
{
	task_t *t = task_get_from_pid(pid);
	
	if(t != NULL) t->state = TASK_STATE_PAUSE;	
}


/**
 * Ristabilisce il processo
 * 
 * \param pid processo
 */
uint32_t task_resume(pid_t pid)
{
	task_t *t = task_get_from_pid(pid);
	
	if(t != NULL) t->state = TASK_STATE_RUNNABLE;		
}



/**
 * Esegue un task
 * 
 * \param func funzione main del task
 * \param argv argomenti
 * \param argc numero argomenti
 * \return pid del task eseguito
 */
pid_t task_exec(int (*func)(int, char **), char **argv, int argc)
{	
	// Alloca il task
	task_t *taskn = task_create();
	
	// Imposta uid
	taskn->uid = 1;
	
	// Imposta il nome
	strcpy(taskn->name, argv[0]);		
	
	// **** Avvia il task occupando tutta la memoria! *****
	//func(argc, argv);
	
	return taskn->pid;
}


/**
 * Uccide un processo
 * 
 * \param pid processo
 */
uint32_t task_kill(pid_t pid)
{

}


/**
 * Crea un task, lo mette in coda
 * 
 * \return task creato
 */
task_t *task_create()
{
	task_t *taskn;
	
	if(task_num >= TASK_MAX_NUM)
		return (task_t *) NULL;	
		
	// Disattiva gli interrupt, altrimenti potrebbe capitare un altro task switch
	// durante le operazioni di inizializzazione. (Non dovrebbero servire perche' le syscall
	// bloccano gia' gli interrupt, ma lo tengo per i test kernel level)
	// TODO sostituire con le funzioni globali per l'architettura generica 
	asm("cli");
	
	// Alloca il task
	taskn = (task_t *) mm_kalloc(sizeof(task_t)*10);
	if(taskn == NULL) return (task_t *) NULL;
	
	// Assegna il pid
	task_cur_pid++;
	taskn->pid = task_cur_pid;
	taskn->state = TASK_STATE_RUNNABLE;
	task_num++;
	
	// Azzera la queue dei messaggi
	taskn->msg_end = taskn->msg_start = NULL;
	taskn->msg_num = 0;
	
	// Aggiunge all'inizio
	taskn->next = task_list;
	task_list = taskn;
	
	// Alloca un terminale
	taskn->term = term_new(TERM_STD_SIZE);
	
	// Se e' l'unico task, lo mette in esecuzione
	if(task_current == NULL) task_current = task_list;
	
	asm("sti");
	
	return (task_t *) taskn;
}
